#pragma once

#include <cstddef>
#include <functional>
#include <utility>
#include <cassert>

/**
 * @brief 一个极简版的共享所有权智能指针（教学示例）。
 *
 * 功能特点：
 *   - 通过控制块（ControlBlock）维护引用计数。
 *   - 支持自定义删除器（Deleter）。
 *   - 支持拷贝、移动、reset、swap 等基础接口。
 *
 * 不足之处：
 *   - 计数器为普通整型，非线程安全。
 *   - 未实现 weak_ptr、别名构造、enable_shared_from_this 等高级特性。
 */
template <typename T>
class SharedPtr {
public:
    using element_type = T;

    /// 默认构造：空指针
    constexpr SharedPtr() noexcept = default;

    /// nullptr 构造：和默认构造效果一样
    constexpr SharedPtr(std::nullptr_t) noexcept {}

    /**
     * @brief 直接接管裸指针，使用 `delete` 作为默认删除器。
     *
     * @param ptr 必须指向 `new` 出来的对象；SharedPtr 将负责释放。
     */
    explicit SharedPtr(T* ptr)
        : ptr_(ptr) {
        if (ptr_) {
            control_ = new ControlBlock(
                ptr_,
                // 默认删除器：直接 delete
                [](T* p) noexcept { delete p; }
            );
        }
    }

    /**
     * @brief 使用自定义删除器。
     *
     * @tparam Deleter 任意可调用类型，形参为 `T*`。
     * @param ptr      被托管的裸指针。
     * @param deleter  指定删除逻辑（例如 fclose / 自定义释放器）。
     */
    template <typename Deleter>
    SharedPtr(T* ptr, Deleter deleter)
        : ptr_(ptr) {
        if (ptr_) {
            control_ = new ControlBlock(ptr_, std::move(deleter));
        }
    }

    /// 拷贝构造：共享同一控制块，引用计数 +1
    SharedPtr(const SharedPtr& other) noexcept
        : ptr_(other.ptr_)
        , control_(other.control_) {
        add_ref();
    }

    /// 移动构造：只转移指针与控制块，无需调整引用计数
    SharedPtr(SharedPtr&& other) noexcept
        : ptr_(other.ptr_)
        , control_(other.control_) {
        other.ptr_ = nullptr;
        other.control_ = nullptr;
    }

    /// 拷贝赋值：先释放自身，再共享对方的控制块
    SharedPtr& operator=(const SharedPtr& other) noexcept {
        if (this != &other) {
            release();
            ptr_ = other.ptr_;
            control_ = other.control_;
            add_ref();
        }
        return *this;
    }

    /// 移动赋值：释放自身后接管对方资源
    SharedPtr& operator=(SharedPtr&& other) noexcept {
        if (this != &other) {
            release();
            ptr_ = other.ptr_;
            control_ = other.control_;
            other.ptr_ = nullptr;
            other.control_ = nullptr;
        }
        return *this;
    }

    /// 析构：引用计数减 1，计数归零时销毁对象并释放控制块
    ~SharedPtr() {
        release();
    }

    /// 交换两个 SharedPtr（不抛异常）
    void swap(SharedPtr& other) noexcept {
        std::swap(ptr_, other.ptr_);
        std::swap(control_, other.control_);
    }

    /// 将当前指针重置为空，引用计数减 1
    void reset() noexcept {
        release();
        ptr_ = nullptr;
        control_ = nullptr;
    }

    /**
     * @brief 将当前指针指向新的裸指针，并重置引用计数。
     * @param ptr 新的裸指针
     */
    void reset(T* ptr) {
        reset();
        if (ptr) {
            control_ = new ControlBlock(ptr, [](T* p) noexcept { delete p; });
            ptr_ = ptr;
        }
    }

    /**
     * @brief 将当前指针指向新的裸指针及自定义删除器。
     * @tparam Deleter 删除器类型
     * @param ptr      新裸指针
     * @param deleter  自定义删除器
     */
    template <typename Deleter>
    void reset(T* ptr, Deleter deleter) {
        reset();
        if (ptr) {
            control_ = new ControlBlock(ptr, std::move(deleter));
            ptr_ = ptr;
        }
    }

    /// 获取当前托管的裸指针
    [[nodiscard]] T* get() const noexcept {
        return ptr_;
    }

    /// 解引用运算符（前提：指针非空）
    T& operator*() const noexcept {
        assert(ptr_ && "Dereferencing nullptr SharedPtr");
        return *ptr_;
    }

    /// 成员访问运算符（前提：指针非空）
    T* operator->() const noexcept {
        assert(ptr_ && "Accessing nullptr SharedPtr");
        return ptr_;
    }

    /// 返回引用计数（若为空指针则为 0）
    [[nodiscard]] std::size_t use_count() const noexcept {
        return control_ ? control_->ref_count : 0;
    }

    /// 判断当前是否是独占拥有者：引用计数为 1 且非空
    [[nodiscard]] bool unique() const noexcept {
        return use_count() == 1;
    }

    /// 布尔上下文：是否托管对象
    explicit operator bool() const noexcept {
        return ptr_ != nullptr;
    }

private:
    /**
     * @brief 控制块：保存引用计数、实际对象指针和删除器。
     */
    struct ControlBlock {
        ControlBlock(T* ptr, std::function<void(T*)> del) noexcept
            : stored_ptr(ptr), deleter(std::move(del)) {}

        /// 调用删除器销毁对象（仅当引用计数归零时触发）
        void destroy() noexcept {
            if (stored_ptr) {
                deleter(stored_ptr);
                stored_ptr = nullptr;
            }
        }

        T* stored_ptr{};                 ///< 被管理的真实指针
        std::size_t ref_count{1};        ///< 强引用计数（初始 1）
        std::function<void(T*)> deleter; ///< 自定义删除器
    };

    /// 引用计数 +1（仅在控制块存在时有效）
    void add_ref() noexcept {
        if (control_) {
            ++control_->ref_count;
        }
    }

    /// 释放当前持有的引用；若计数归零则销毁对象并释放控制块
    void release() noexcept {
        if (control_) {
            if (--control_->ref_count == 0) {
                control_->destroy();
                delete control_;
            }
            control_ = nullptr;
        }
        ptr_ = nullptr;
    }

private:
    T* ptr_{nullptr};            ///< 当前托管的裸指针
    ControlBlock* control_{nullptr}; ///< 对应的控制块
};

/// make_shared 的简易版本：构造对象并返回 SharedPtr
template <typename T, typename... Args>
SharedPtr<T> make_shared(Args&&... args) {
    return SharedPtr<T>(new T(std::forward<Args>(args)...));
}